package q7_gc_test;

import java.util.Random;

/*
GC垃圾回收算法与垃圾收集器的关系？分别是什么请你谈谈？
1、GC算法（引用计数、复制、标清、标整）是内存回收的方法论，垃圾收集器就是算法落地实现。
2、因为目前为止还没有完美的垃圾收集器出现，更加没有万能的垃圾收集器，只是针对具体应用最合适的收集器，进行分代收集。
3、4种主要垃圾收集器
    1、Serial(串行垃圾回收器)
        它为单线程环境设计且只使用一个线程进行垃圾回收，会暂停所有的用户线程。所以不适合服务器环境。
    2、Parallel(并行垃圾回收器)
        多个垃圾收集线程并行工作 ，此时用户线程是暂停的，适用于科学计算、大数据处理首台处理等弱交互场景。
    3、CMS(并发垃圾回收器)
        用户线程和垃圾收集线程同时执行（不一定是并行，可能交替执行），不需要停顿用户线程。
        互联网公司多用它，适用对响应时间有要求的场景。
    4、G1
        G1垃圾回收器将堆内存分割成不同的区域然后并发的对其进行垃圾回收
    5、ZGC（jdk11/12）

怎么查看服务器默认的垃圾收集器是哪个？
    java -XX:+PrintCommandLineFlags -version
    JDK8默认的垃圾收集器: -XX:+UseParallelGC

    查看某个特性是否开启
        jinfo flag UseParallelGC 1027

默认的垃圾收集器有哪些？
    UseSerialGC、UseParallelGC、UseParNewGC、UseConcMarkSweepGC、UseParallelOldGC、UseG1GC
    UseSerialOldGC已废弃

垃圾收集器
    Young Gen : Serial Copying(SerialGC)、Parallel Scavenge(ParallelGC)、ParNew(ParNewGC)
    Old Gen : Serial MSC(SerialOldGC)、Parallel Compacting(ParallelOldGC)、CMS(ConcMarkSweepGC)
    G1两者都覆盖

    1、部分参数预先说明
        1、DefNew (Default New Generation)
        2、Tenured (Old)
        3、ParNew (Parallel New Generation)
        4、PSYoungGen (Parallel Scavenge)
        5、ParOldGen (Parallel Old Generation)
    2、Server/Client模式分别是什么意思
        1、适用范围：只需要掌握Server模式即可，Client 模式基本不会用
        2、操作系统
            1、32位Windows操作系统，不论硬件如何都默认使用Client的JVM模式
            2、32位其他操作系统，2G内存同时有2个CPU以上用Server模式，低于该配置还是Client模式
            3、64位only Server模式
    3、新生代
        1、串行GC(Serial)/(Serial Copying)
            1、一句话：一个单线程的收集器，在进行垃圾收集时候，必须暂停其他所有的工作线程直到它收集结束。
            2、串行收集器组合Serial + Serial Old
            3、串行收集器是最古老，最稳定以及效率高的收集器，只使用一个线程去回收，但其在进行垃圾收集过程中可能会产生较长的停顿（Stop-The-World状态）。
            虽然在收集垃圾过程中需要暂停所有其他的工作线程，但是它简单高效，对于限定单个CPUT环境来说，没有线程交互的开销可以获得最高的单线程垃圾收集效率，
            因此Serial垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器。
            4、对应的JVM参数是：-XX:+UseSerialGC
            开启后使用：Serial（Young区用） + Serial Old（Old区用）的收集器组合
            表示：新生代、老年代都会使用串行回收收集器，新生代使用复制算法，老年代使用标记-整理算法
            JVM参数示例：-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseSerialGC
        2、并行GC(ParNew)
            1、一句话：使用多线程进行垃圾回收，在垃圾收集时，会Stop-The-World暂停其他所有工作线程直到其他收集结束。
            2、PraNew收集器其实就是Serial收集器新生代的并行多线程版本，最常见的应用场景是配合老年代的CMS GC工作，其余的行为和
            Serial收集器完全一样，PraNew垃圾收集器在垃圾收集过程中同样也要暂停所有其他的工作线程。它是很多java虚拟机运行在
            Server模式下新生代的默认垃圾收集器。
            3、常用对应JVM参数：-XX:+UseParNewGC  启用ParNew收集器，只影响新生代，不影响老年代
            开启上述参数后，会使用：ParNew(Young区用) + Serial Old（Old区用）的收集器组合，新生代使用复制算法，老年代使用标记-整理算法。
            4、但是ParNew+Tenured这样的搭配，java8已经不再被推荐
            Java HotSpot(TM) 64-Bit Server VM warning:
            Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release.
            5、-XX:ParallelGCThreads 限制线程数量，默认开户和CPU数目相同的线程数
            JVM参数示例：-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParNewGC -XX:ParallelGCThreads=8
        3、并行回收GC(Parallel)/(Parallel Scavenge)
            1、Parallel Scavenge收集器类似ParNew也是一个新生代垃圾收集器，使用复制算法，也是一个并行的多线程的垃圾收集器，俗称吞吐量优先收集器。
            一句话：串行收集器在新生代和老年代的并行化。
            2、它重点关注的是
                1、可控制的吞吐量（Thoughput=运行用户代码时间/(运行用户代码时间+垃圾收集时间)，也即比如程序运行100分钟，垃圾收集时间1分钟，吞吐量就是99%）。
                高吞吐量意味着高效利用CPU的时间，它用于在后台运算而不需要太多交互的任务。
                2、自适应策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。（自适应调节策略：虚拟机会根据当前系统的运行情况收集性能监控信息，动态调整这些参数以提供最合适的停顿时间（-XX:MaxGCPauseMillis）或最大的吞吐量）
            3、常用JVM参数：-XX:+UseParallelGC或-XX:+UseParallelOldGC（可互相激活）使用Parallel Scavenge收集器
               开启该参数后：新生代使用复制算法，老年代使用标记-整理算法
            4、-XX:ParallelGCThreads=数字N  表示启动多少个GC线程
               CPU>8 N=5/8
               CPU<8 N=实际CPU个数
               JVM参数示例：
                    -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParallelGC
                    -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParallelOldGC
    4、老年代
        1、串行GC(Serial Old)/(Serial MSC)
            1、Serial Old是Serial垃圾收集器老年代版本，它同样是个单线程的收集器，使用标记-整理算法，
            这个收集器也主要是运行在Client默认的java虚拟机默认的老年代垃圾收集器。
            2、在Server模式下，主要有两个用途（了解，版本已经到8及以后）：
                1、在JDK1.5之前版本中与新生代的Parallel Scavenge收集器搭配使用。（Parallel Scavenge + Serial Old）
                2、作为老年代版中使用CMS收集器的后备垃圾收集方案。
        2、并行GC(Parallel Old)/(Parallel MSC)
            1、Parallel Old收集器是Parallel Scavenge的老年代版本，使用多线程的标记-整理算法，Parallel Old收集器在JDK1.6才开始提供。
            2、在JDK1.6之前，新生代使用Parallel Scavenge收集器只能搭配老年代的Serial Old收集器，只能保证新生代的吞吐量优先，
               无法保证Parallel Scavenge和老年代Parallel Old收集器的搭配策略。在JDK1.8及后（Parallel Scavenge +　Parallel Old）
            3、JVM常用参数：
               -XX:+UseParallelOldGC 使用Parallel Old收集器，设置该参数后，新生代Parallel + 老年代Parallel Old
        3、并发标记清除GC（CMS）
            1、CMS收集器（Concurent Mark Sweep: 并发标记清除）是一种获取最短回收停顿时间为目标的收集器。
            适合应用在互联网或者B/S系统的服务器上，这类应用尤其重视服务的响应速度，希望系统停顿时间最短。
            CMS非常适合堆内存大、CPU核数多的服务器端应用，也是G1出现之前大型应用的首先收集器。
            2、Concurent Mark Sweep 并发标记清除，并发收集低停顿，并发指的是与用户线程一起执行。
            3、开启该收集器的JVM参数：-XX:+UseConcMarkSweepGC 开启该参数后会自动将 -XX:+UseParNewGC 打开。
            开启该参数后，使用ParNew（Young区）+ CMS（Old区）+ Serial Old的收集器组合， Serial Old将作为CMS出错的后备收集器
            -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC
            4、CMS收集器-四过程
                1、初始标记（CMS initial mark）短暂STW
                    只是标记一下GC Roots能直接关联的对象，速度很快，仍然需要暂停所有的工作线程。
                2、并发标记（CMS concurrent mark）和用户线程一起
                    进行GC Roots跟踪的过程，和用户线程一起工作，不需要暂停工作线程。主要标记过程，标记全部对象。
                3、重新标记（CMS remark）短暂STW
                    为了修正在并发标记期间，因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录，仍然需要暂停所有的工作线程。
                    由于并发标记时，用户线程依然运行，因此正式清理前，再做修正。
                4、并发清除（CMS concurrent sweep）和用户线程一起
                    清除GC Roots不可达对象，和用户线程一起工作，不需要暂停工作线程。基于标记结果，直接清理对象
                    由于耗时最长的并发标记和并发清除过程中，垃圾收集线程可以和用户现在一起并发工作，所以总体上来看CMS收集器的内存回收和用户线程是一起并发地执行。
            5、优缺点
                优点：
                    并发收集低停顿
                缺点：
                    1、并发执行，对CPU资源压力大
                        由于并发进行，CMS在收集与应用线程会同时增加对堆内存的占用，也就是说，CMS必须要在老年代堆内存用尽之前完成垃圾回收，
                        否则CMS回收失败时，将触发担保机制，串行老年代收集器将会以STW的方式进行一次GC，从而造成较大停顿时间。
                    2、采用的标记清除算法会导致大量碎片
                        标记清除算法无法整理空间碎片，老年代空间会随着应用时长被逐步耗尽，最后将不得不通过担保机制对堆内存进行压缩。CMS也提供了参数
                        -XX:CMSFullGCsBeForeCompaction（默认0，即每次都进行内存整理）来指定多少次CMS收集之后，进行一次压缩的FullGC。
    5、垃圾收集器配置代码总结
        1、底层代码
        bool Arguments::check_gc_consistency(){
            bool status = true;
            uint i = 0;
            if(UseSerialGC)                       i++;
            if(UseConcMarkSweepGC || UseParNewGC) i++;
            if(UseParallelGC || UseParallelOldGC) i++;
            if(UseG1GC)                           i++;
            if(i > 1){
                jio_fpintf(defaultStream::error_stream(),
                            "Conflicting collector combinations in option list; ",
                            "please refer to the release notes for the combinations",
                            "allowed\n");
                status = false;
            }
            return status;
        }
        2、实际代码
        GCAlgorithm

生产上如何配置垃圾收集器的？如何选择垃圾收集器？
组合选择
    1、单CPU或小内存，单机程序
        -XX:+UseSerialGC
    2、多CPU，需要最大吞吐量，如后台计算型应用
        -XX:+UseParallelGC
        或
        -XX:+UseParallelOldGC
    3、多CPU，追求低停顿时间，需快速响应如互联网应用
        -XX:+UseConcMarkSweepGC
        -XX:+UseParNewGC

G1垃圾收集器
1、以前收集器的特点
    年轻代和老年代是各自独立且连续的内存块
    年轻代收集使用单eden+S0+S1进行复制算法
    老年代收集必须扫描整个老年代区域
    都是以尽可能少而快速地执行GC为设计原则
2、G1是什么？
    1、G1(Garbage-First)收集器，是一款面向服务端应用的收集器
    2、从官网的描述中，我们知道G1是一种服务器端的垃圾收集器，应用在多处理器和大容量内存环境中，在实现高吞吐量的同时，尽可能的满足垃圾收集暂停时间的要求。另外，它还具有以下特性：
        1、像CMS收集器一样，能与应用程序线程并发执行
        2、整理空闲空间更快
        3、需要更多的时间来预测GC停顿时间
        4、不希望牺牲大量的吞吐性能
        5、不需要更大的JavaHeap
    3、G1收集器的设计目标是取代CMS收集器，它同CMS相比，在以下方面表现更出色：
        G1是一个有整理内存过程的垃圾收集器，不会产生很多内存碎片。
        G1的Stop-The-World(STW)更可控，G1在停顿时间上添加了预测机制，用户可以指定期望停顿时间。
    4、CMS垃圾收集器虽然减少了暂停应用程序的运行时间，但是它还是存在着内存碎片问题，于是，为了去除内存碎片问题，同时又保留CMS垃圾收集器低暂停时间的优点，Java7发布了一个新的垃圾收集器-G1垃圾收集器
    5、G1是在2012年才在jdk1.7u4中可用。oracle官方计划在jdk9中将G1变成默认的垃圾收集器以替代CMS。它是一款面向服务端应用的收集器，主要应用在多CPUT和大内存服务环境下，极大的减少垃圾收集的停顿时间，全面提升服务器的性能，逐步替换java8以前的CMS收集器。
    6、主要改变是Eden，Survivor和Tenured等内存区域不再是连续的了，而是变成一个个大小一样的region，每个region从1M到32M不等。一个region有可能属于Eden，Survivor和Tenured内存区域。
    7、特点：
        1、G1能充分利用多CPU，多核环境硬件优势，尽量缩短STW。
        2、G1整个上采用标记-整理算法，局部是通过复制算法，不会产生内存碎片。
        3、宏观上看G1之中不再区分年轻代和老年代。把内存划分成多个独立的子区域（Region），可以近似理解为一个围棋的棋盘。
        4、G1收集器里面讲整个的内存区都混在一起了，但其本身依然在范围内要进行年轻代和老年代的区分，保留了新生代和老年代，但它们不再是物理隔离的，而是一部分Region的集合且不需要Region是连续的，也就是说依然会采用不同的GC方式来处理不同的区域。
        5、G1虽然也是分代收集器，但整个内存分区不存在物理上的年轻代和老年代的区别，也不需要完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念，或者说每个分区都可能随G1的运行在不同代之间前后切换。
3、底层原理
    1、Region区域化垃圾收集器（最大好处是化整为零，避免全内存扫描，只需要按照区域来进行扫描即可）
        1、区域化内存划片Region，整体编为了一些列不连续的内存区域，避免了全内存区的GC操作。
        2、核心思想是将整个堆内存区域分成大小相同的子区域（Region），在JVM启动时会自动设置这此子区域的大小
        在堆使用上，G1并不要求对象的存储一定是物理上连续的只要逻辑上连续即可，每个分区也不会固定地为某个代服务，可以按需在年轻代和老年代之间切换。启动时可以通过参数-XX:G1HeapRegionSize=n可指定分区大小（1MB~32MB，且必须是2的幂），默认将整堆划分为2048个分区。
        大小范围在1MB~32MB，最多能设置2048个区域，也即能够支持的最大内存为：32MB * 2048 = 65536MB = 64G 内存。
    2、回收步骤
        G1收集器下的Young GC
        针对Eden区进行收集，Eden区耗尽后会被触发，主要是小区域收集+形成连续的内存块，避免内存碎片
            1、Eden区的数据移动到Survivor区，假如出现Survivor区空间不够，Eden区数据会全部晋升到Old区
            2、Survivor区的数据移动新的Survivor区，部分数据晋升到Old区
            3、最后Eden区收拾干净了，GC结束，用户的应用程序继续执行。
    3、4步过程
        1、初始标记：只标记GC Roots能直接关联到的对象
        2、并发标记：进行GC Roots Tracing的过程
        3、最终标记：修正并发标记期间，因程序运行导致标记发生变化的那一部分对象
        4、筛选回收：根据时间来进行价值最大化的回收
4、case案例
    GCAlgorithm
    -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC
5、常用配置参数（了解）
    -XX:+UseG1GC
    -XX:G1HeapRegionSize=n：设置G1区域的大小。值是2的幂，范围是1MB~32MB。目标是根据最小的java堆大小划分出约2048个区域。
    -XX:MaxGCPauseMillis=n: 最大GC停顿时间，这个是软目标，JVM将尽可能（但不保证）停顿小于这个时间。
    -XX:InitialHeapOccupancyPercent=n: 堆占用了多少的时候就触发GC，默认为45。
    -XX:ConcGCThreads=n: 并发GC使用的线程数。
    -XX:G1ReservePercent=n: 设置作为空闲空间的预留内存对比，以降低目标空间溢出的风险，默认值为10%。

    开发人员仅仅需要声明以下参数即可：
        三步归纳：开始G1+设置最大内存+设置最大停顿时间
        -XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=100
6、和CMS相比的优势
比起CMS有两个优势
    1、G1不会产生内存碎片。
    2、是可以精确控制停顿。该收集器是把整个堆（新生代、老年代）划分成多个固定大小的区域，每次根据允许停顿的时间去收集垃圾最多的区域。
7、小总结

 */
public class GCAlgorithm {

    /*
    1、-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC       (DefNew + Tenured)

    2、-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC       (ParNew + Tenured)

    3、-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC       (PSYoungGen + ParOldGen)

    4、-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldGC       (PSYoungGen + ParOldGen)
    不加默认就是UseParallelGC
    -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags

    5、-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC       (ParNew + CMS)

    6、-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC       (garbage-first heap)

    7、理论知道即可，实际中已经被优化掉了，没有了
    -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialOldGC
     */
    public static void main(String[] args) {

        System.out.println("GC算法演示");
        try{
            String str = "str";
            while (true){
                str += str + new Random().nextInt(77777777)  + new Random().nextInt(88888888);
                str.intern();
            }
        }catch(Throwable e){
            e.printStackTrace();
        }

    }
}
